﻿using System;
using System.Diagnostics;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using FlitBit.Core;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FlitBit.Logging.Tests
{
	[TestClass]
	public class LogSinkTests
	{
		class Ghost: ILogSinkGhostWriter
		{
			readonly Action<LogEvent> _observer;

			public Ghost(Action<LogEvent> observer)
			{
				this._observer = observer;
			}
			/// <summary>
			///   Delegates writing an event.
			/// </summary>
			/// <param name="writer"></param>
			/// <param name="evt"></param>
			public void GhostWrite(LogEventWriter writer, LogEvent evt)
			{
				_observer(evt);
			}			
			
		}

		[TestMethod]
		public void LogSink_LogsCritical()
		{
			var args = new
			{
				Delg = new Func<string>(() => "This is a message."),
				Message = "This is a message too.",
				Formatted = "This message says: {0}.",
				Data = new { Something = "This is something." }
			};
			string latest = null;
			TraceEventType evtType = default(TraceEventType);
			var sink = new LogSink(new Ghost(evt =>
			{
				latest = evt.Message;
				evtType = evt.EventType;
			}), "Test", SourceLevels.All, TraceEventType.Warning,
														new TraceLogEventWriter(), null);

			sink.Critical(args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

			sink.Critical(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Critical, evtType);

		}

		[TestMethod]
		public void LogSink_LogsErrors()
		{
			var args = new
			{
				Delg = new Func<string>(() => "This is a message."),
				Message = "This is a message too.",
				Formatted = "This message says: {0}.",
				Data = new { Something = "This is something." }
			};
			string latest = null;
			TraceEventType evtType = default(TraceEventType);
			var sink = new LogSink(new Ghost(evt =>
			{
				latest = evt.Message;
				evtType = evt.EventType;
			}), "Test", SourceLevels.All, TraceEventType.Warning,
														new TraceLogEventWriter(), null);

			sink.Error(null);
			sink.Error(default(string));
			sink.Error(default(string), default(string));
			
			sink.Error(args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

			sink.Error(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Error, evtType);

		}

		[TestMethod]
		public void LogSink_LogsWarnings()
		{
			var args = new
			{
				Delg = new Func<string>(() => "This is a message."),
				Message = "This is a message too.",
				Formatted = "This message says: {0}.",
				Data = new { Something = "This is something." }
			};
			string latest = null;
			TraceEventType evtType = default(TraceEventType);
			var sink = new LogSink(new Ghost(evt =>
			{
				latest = evt.Message;
				evtType = evt.EventType;
			}), "Test", SourceLevels.All, TraceEventType.Warning,
														new TraceLogEventWriter(), null);

			sink.Warning(null);
			sink.Warning(default(string));
			sink.Warning(default(string), default(string));
			
			sink.Warning(args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

			sink.Warning(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Warning, evtType);

		}

		[TestMethod]
		public void LogSink_LogsInformations()
		{
			var args = new
			{
				Delg = new Func<string>(() => "This is a message."),
				Message = "This is a message too.",
				Formatted = "This message says: {0}.",
				Data = new { Something = "This is something." }
			};
			string latest = null;
			TraceEventType evtType = default(TraceEventType);
			var sink = new LogSink(new Ghost(evt =>
			{
				latest = evt.Message;
				evtType = evt.EventType;
			}), "Test", SourceLevels.All, TraceEventType.Warning,
														new TraceLogEventWriter(), null);

			sink.Information(null);
			sink.Information(default(string));
			sink.Information(default(string), default(string));

			sink.Information(args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Delg);
			Assert.AreEqual(args.Delg(), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Message);
			Assert.AreEqual(args.Message, latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Formatted, args.Message);
			Assert.AreEqual(String.Format(args.Formatted, args.Message), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

			sink.Information(LogSink.DefaultApplicationSpecificLogEventKind, LogSink.DefaultApplicationSpecificLogEventName, args.Data);
			Assert.AreEqual(args.Data.ToJson(), latest);
			Assert.AreEqual(TraceEventType.Information, evtType);

		}
	}
}
